iT邦幫忙

2024 iThome 鐵人賽

DAY 15
2
Kubernetes

都什麼年代了,還在學 Kubernetes系列 第 15

學 Kubernetes 的第十五天 - Storage - ConfigMap

  • 分享至 

  • xImage
  •  

ConfigMap 是 Kubernetes 中的一種資源,用來將非機密的設定資料(如設定檔案、環境變數)與容器分離。這使得應用程式可以在不同的環境中更靈活地使用相同的映像檔,而不需要將設定硬編碼到映像檔中。

為什麼需要 ConfigMap

ConfigMap 的主要目的是讓應用程式設定和容器鏡像解耦。這樣做的好處是:

  • 環境獨立性:應用程式的設定可以根據環境(開發、測試、生產)來變動,而不需要重新構建容器映像。
  • 配置管理:集中管理設定資料,方便對應用程式的配置進行修改和版本控制。
  • 動態配置:可以在不重建和重新部署映像的情況下更新應用程式的配置。

應用場景

  1. 環境變數:將應用程式所需的環境變數存儲在 ConfigMap 中,在 Pod 啟動時注入到容器中。
  2. 設定檔案:將應用程式的設定檔案內容存儲在 ConfigMap 中,並掛載到容器的檔案系統中。
  3. 命令列引數:使用 ConfigMap 來動態調整容器的命令列引數。
  4. 共享配置:在多個 Pod 之間共享相同的設定資料。

注意事項

在 Pod 中使用 ConfigMap,通常有兩種做法: 掛載為文件設為環境變量。這兩種做法有不同的更新策略:

  • 更新 ConfigMap 後,使用該 ConfigMap 的 Pod 不會自動更新
  • 對於環境變量,需要重啟 Pod
  • 對於掛載為文件的情況,Kubernetes 會自動更新(可能有延遲)

組態檔案說明

一個簡單的 ConfigMap 組態檔案如下:

apiVersion: v1
kind: ConfigMap
metadata:
  name: my-config
data:
  # 簡單的 key-value 配對
  app.properties: |
    key1=value1
    key2=value2
  
  # 或者可以單獨列出多個鍵
  database_url: "jdbc:mysql://dbhost:3306/mydb"
  database_user: "admin" 
  database_password: "password"
  • apiVersion: v1:指定使用的 Kubernetes API 版本為 v1。
  • kind: ConfigMap:指定這個資源的類型是 ConfigMap。
  • metadata
    • name: my-config:ConfigMap 的名稱是 my-config
  • data:ConfigMap 包含的數據。
    • app.properties:一個多行屬性文件,用來存儲簡單的 key-value 配對。
      • key1=value1
      • key2=value2
    • database_url:指定數據庫的連接 URL。
    • database_user:指定數據庫的用戶名。
    • database_password:指定數據庫的密碼。

在這個範例中,my-config 是 ConfigMap 的名稱。data 區塊包含了應用程式所需的配置資料。這些資料可以在 Pod 中以環境變數、命令列引數或檔案的形式使用。

透過 ConfigMap,Kubernetes 提供了一個靈活的機制來管理和分發應用程式的設定資料,使得應用程式能夠更容易適應不同的運行環境。

實作

組態檔案: cm.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
data:
  debug: "false"
  log_level: INFO
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-config
data:
  user: vincent
  password: vincent_12345678

  init.sql: |
    -- create
    CREATE TABLE USER (
      id INT PRIMARY KEY,
      user_id varchar(100),
      login_date DATE
    );

    -- insert
    INSERT INTO USER VALUES (1, 'a1', '1970-01-01 00:00:00');
    INSERT INTO USER VALUES (2, 'a2', '2024-04-07 00:00:00');
    INSERT INTO USER VALUES (3, 'a3', '1990-01-01 00:00:00');

這個組態檔定義了兩個 Kubernetes ConfigMap 資源。

  • env-config 這個 ConfigMap 主要用於存儲應用程式的環境配置,通常用於在容器內通過環境變數或掛載檔案的形式注入到應用程式中。

  • mysql-config 這個 ConfigMap 可能會用於初始化 MySQL 資料庫,例如在容器啟動時運行該 SQL 腳本來設定資料庫結構和初始數據。

  • 建立 ConfigMap

kubectl create -f cm.yaml

使用來自多個 ConfigMap 的資料定義容器環境變數

組態檔案: pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/sh", "-c", "env" ]
      env:
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: env-config
              key: log_level
        - name: MYSQL_USER
          valueFrom:
            configMapKeyRef:
              name: mysql-config
              key: user
  restartPolicy: Never
  • spec.containers.env:定義環境變量。

    • LOG_LEVEL:從 env-config ConfigMap 中提取 log_level 鍵的值,並將其設置為環境變量 LOG_LEVEL
    • MYSQL_USER:從 mysql-config ConfigMap 中提取 user 鍵的值,並將其設置為環境變量 MYSQL_USER
  • 部署 Pod

kubectl apply -f pod.yaml
  • 用 logs 指令查看輸出
kubectl logs dapi-test-pod
---
[...]
LOG_LEVEL=INFO
[...]
MYSQL_USER=vincent
[...]

可以看到 env 裡包含我們引用的環境變數

  • 刪除 pod
kubectl delete -f pod.yaml --now

在 Pod 命令中使用 ConfigMap 定義的環境變數

在 pod 指令參數 (command, args) 中,可以使用 $(VAR_NAME) 引用 ConfigMap 定義的環境變數。

組態檔案: pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/echo", "$(LOG_LEVEL) $(MYSQL_USER)" ]
      env:
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: env-config
              key: log_level
        - name: MYSQL_USER
          valueFrom:
            configMapKeyRef:
              name: mysql-config
              key: user
  restartPolicy: Never

command: [ "/bin/echo", "$(LOG_LEVEL) $(MYSQL_USER)" ]:此命令將輸出環境變量 LOG_LEVELMYSQL_USER 的值。這些值是從指定的 ConfigMap 中提取的。

  • 部署 Pod
kubectl apply -f pod.yaml
  • 用 logs 指令查看輸出
kubectl logs dapi-test-pod
---
INFO vincent

可以看到 command 可以正確參考環境變數

  • 刪除 pod
kubectl delete -f pod.yaml --now

使用儲存在 ConfigMap 中的資料填充 Volume

在引用 Volume 時,K8s 也提供 ConfigMap 來源,當我們使用 ConfigMap 當作 Volume 來源時,K8S 會在我們指定的目錄中產生對應的檔案: ConfigMap 的 Key 為檔名,ConfigMap 的 Value 為檔案的內容。

組態檔案: pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/sh", "-c", "ls /mnt/data; while true; do echo ''; sleep 3600; done" ]
      volumeMounts:
      - name: config-volume
        mountPath: /mnt/data
  volumes:
    - name: config-volume
      configMap:
        # 提供包含要新增到容器中的檔案的 ConfigMap 的名稱
        name: mysql-config
  restartPolicy: Never
  • containers[].volumeMounts
    • name: config-volume:這個卷是從 ConfigMap mysql-config 中獲取的,並掛載到容器內的 /mnt/data 目錄。
    • 當這個 Pod 啟動時,mysql-config ConfigMap 中的所有鍵值對將會作為文件映射到 /mnt/data 目錄。每個鍵將變成一個文件名,對應的值將成為文件內容。
  • volumes
    • configMap:指定使用 ConfigMap mysql-config 來創建一個卷。該 ConfigMap 的內容會以文件的形式存儲在卷中,並掛載到容器的指定目錄。

補充:
執行完 ls /mnt/data 指令後,實際上是列出文件后立即退出,會導致容器完成任務而退出,變成 Complete。為了避免 pod 立即結束,在 command 後面加上 while true; do echo ''; sleep 3600; done" 無窮迴圈是一個解決方法。

  • 部署 Pod
kubectl apply -f pod.yaml
  • 用 logs 指令查看輸出
kubectl logs dapi-test-pod
---
init.sql
password
user

可以看到 /mnt/data 路徑有我們在 mysql-config 定義,以鍵值為名的檔案

  • 使用 kubectl exec 指令查詢該檔案
kubectl exec dapi-test-pod -- sh -c 'cat /mnt/data/password'
---
vincent_12345678
kubectl exec dapi-test-pod -- sh -c 'cat /mnt/data/init.sql'
---
-- create
CREATE TABLE USER (
  id INT PRIMARY KEY,
  user_id varchar(100),
  login_date DATE
);

-- insert
INSERT INTO USER VALUES (1, 'a1', '1970-01-01 00:00:00');
INSERT INTO USER VALUES (2, 'a2', '2024-04-07 00:00:00');
INSERT INTO USER VALUES (3, 'a3', '1990-01-01 00:00:00');

可以看到 value 就在裡面

  • 刪除 pod
kubectl delete -f pod.yaml --now

將 ConfigMap 資料新增到卷中的特定路徑

組態檔案: pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/sh","-c","ls /mnt/data; while true; do echo ''; sleep 3600; done" ]
      volumeMounts:
      - name: config-volume
        mountPath: /mnt/data
  volumes:
    - name: config-volume
      configMap:
        name: mysql-config
        items:
        - key: user
          path: mysql_user
  restartPolicy: Never
  • volumes.configMap
    • name: mysql-config:指定 ConfigMap 的名稱為 mysql-config
    • items
      • key: user:表示只從 mysql-config 中取出 user 鍵。
      • path: mysql_user:將 user 鍵的內容存儲到 /mnt/data/mysql_user 文件中。這樣,容器內的 /mnt/data/mysql_user 將包含 user 鍵的值,即 vincent
  • 部署 Pod
kubectl apply -f pod.yaml
  • 用 logs 指令查看輸出
kubectl logs dapi-test-pod
---
mysql_user

可以看到 /mnt/data 路徑有重新命名後的物件名稱

  • 查詢該檔案,
kubectl exec dapi-test-pod -- sh -c 'cat /mnt/data/mysql_user'
---
vincent

內容對應的的確是 mysql-config/user

  • 刪除 pod
kubectl delete -f pod.yaml --now

ConfigMap 的自動更新

從上面的練習可知,Pod 可以透過設定環境變數、掛載 Volume 兩種方式引用 ConfigMap 內容。

當已掛載的 ConfigMap 被更新時,所投射的內容最終也會被更新。不過這僅限於使用 Volume 掛載的方式,若是透過環境變數的 Pod ,只有等到 Pod 重啟時才會更新投射的 ConfigMap 內容。

不過掛載 Volume 也不是馬上就會更新,而是 kubelet 同步週期(默認為 1 分鐘)+ kubelet 中 ConfigMap 快取的 TTL(默認為 1 分鐘),也可以透過更新 Pod 立即刷新。

我們可以來實踐一下:

組態檔案: pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: volume-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/sh", "-c", "sleep 3600" ]
      volumeMounts:
      - name: config-volume
        mountPath: /mnt/data
  volumes:
    - name: config-volume
      configMap:
        name: env-config
        items:
        - key: log_level
          path: log_level
  restartPolicy: Never
---
apiVersion: v1
kind: Pod
metadata:
  name: env-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/sh", "-c", "sleep 3600" ]
      env:
        - name: log_level
          valueFrom:
            configMapKeyRef:
              name: env-config
              key: log_level
  restartPolicy: Never
  • 查詢兩個 Pod 投射的 log_level
kubectl exec volume-pod -- sh -c 'cat /mnt/data/log_level'
---
INFO
kubectl exec env-pod -- sh -c 'echo "$log_level"'
---
INFO
  • 使用 kubectl pathc 指令修改 log_level 的內容
kubectl patch configmap env-config -p '{"data":{"log_level":"ERROR"}}'
  • 等待約一分鐘,再次查詢兩個 pod 投射的 log_level
kubectl exec volume-pod -- sh -c 'cat /mnt/data/log_level'
---
ERROR
kubectl env-pod -- sh -c 'echo "$log_level"'evel'
---
INFO

小結

ConfigMap 提供了一個靈活且高效的方式來管理和分發應用程式的設定資料,使 Kubernetes 中的應用部署更加模組化和可維護。通過本文的實例操作,各位可以掌握 ConfigMap 的基本使用方法及其在實際場景中的應用技巧。


上一篇
學 Kubernetes 的第十四天 - Storage - emptyDir & hostPath
下一篇
學 Kubernetes 的第十六天 - Storage - Secret
系列文
都什麼年代了,還在學 Kubernetes37
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言